package com.mianle.userapi.config;

import com.mianle.userapi.util.JsonResponse;
import com.mianle.userapi.util.JsonResponseData;
import com.mianle.userapi.util.JsonResponseEnum;
import com.mianle.userapi.util.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {



    @ResponseBody
    @ExceptionHandler(Throwable.class)
    public Object handleException(Throwable throwable){


        //进行Biz异常处理
        if(throwable instanceof ServiceException){

            ServiceException bizException = (ServiceException) throwable;
            Object result = bizException.getMessage();
            return handleBizResult(result);
        }



        if(throwable instanceof ConstraintViolationException){
            ConstraintViolationException cve = (ConstraintViolationException) throwable;
            Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();

            if(!CollectionUtils.isEmpty(constraintViolations)){

                ConstraintViolation<?> constraintViolation = constraintViolations.iterator().next();
                Object requestInvalidVal = constraintViolation.getInvalidValue();
                String paramStr = constraintViolation.getPropertyPath().toString();

                return formatParamError(requestInvalidVal,paramStr);
            }

        }

        if(throwable instanceof BindException){

            BindException be = (BindException) throwable;
            FieldError fieldError = be.getFieldError();
            return formatParamError(fieldError.getRejectedValue(),fieldError.getField());
        }

        if(throwable instanceof MethodArgumentNotValidException){

            MethodArgumentNotValidException mave = (MethodArgumentNotValidException) throwable;
            FieldError fieldError = mave.getBindingResult().getFieldError();

            return formatParamError(fieldError.getRejectedValue(),fieldError.getField());
        }

        if(throwable instanceof MissingServletRequestParameterException){

            MissingServletRequestParameterException mp = (MissingServletRequestParameterException) throwable;
            return formatParamError(null,mp.getParameterName());
        }

        if(throwable instanceof ServletRequestBindingException){

            ServletRequestBindingException sre = (ServletRequestBindingException) throwable;
            JsonResponseData resultVo =  JsonResponseEnum.PARAM_BIND_ERROR.formatMsg(sre.getMessage());
            return convertIfApplicable(resultVo);
        }


        //相应参数不满足Content-Type 例如：POST-JSON中，Body的JSON字符串不符合规范。
        if(throwable instanceof HttpMessageNotReadableException){

            HttpMessageNotReadableException hmne = (HttpMessageNotReadableException) throwable;
            JsonResponseData resultVo = JsonResponseEnum.HTTP_MESSAGE_CONVERT_FAIL.formatMsg(hmne.getMessage());
            return convertIfApplicable(resultVo);

        }

        log.error("The request occur Inner Exception.",throwable);

        JsonResponseData serverInnerError = JsonResponseEnum.SERVER_INNER_ERROR.result();

        return convertIfApplicable(serverInnerError);
    }



    private Object convertIfApplicable(JsonResponseData resultVo){
        return resultVo;
    }


    private Object formatParamError(Object val, String param){
        JsonResponseData paramErrorResult = JsonResponseEnum.PARAM_INVALID.formatMsg(val,param);
        return convertIfApplicable(paramErrorResult);
    }


    private Object handleBizResult(Object result){
        if(result instanceof JsonResponse){
            return ((JsonResponse) result).result();
        }

        return result;
    }






}
